/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\scanmethod.h"

//---------------------------------------------------------------------------

ScanMethod::ScanMethod(void)
{
	myType      = __wmtNone;
	myMimic     = __mosRandom;

	isStarted   = false;

	packets     = NULL;
	scanPackCnt = 0;

	lastScanTime = scanTime = 0;

	winPcap     = NULL;
	config      = NULL;
	addrList    = NULL;

	scanParameters.Empty();

	waitTime    = 1;

	wmiWorker   = NULL;

	capPackSize = MAX_CAPTURED_PACKET_SIZE;

	arpReplySignature[0] = 0x08;
	arpReplySignature[1] = 0x06;
	arpReplySignature[2] = 0x00;
	arpReplySignature[3] = 0x01;
	arpReplySignature[4] = 0x08;
	arpReplySignature[5] = 0x00;
	arpReplySignature[6] = 0x06;
	arpReplySignature[7] = 0x04;
	arpReplySignature[8] = 0x00;
	arpReplySignature[9] = 0x02;

	mt19937.SeedMT((unsigned long)GetTickCount());
	rndIdx = -1;

	workPhase = __wptNone;

	hWnd = NULL;

	packetBuilderEx      = NULL;
	packetBuilderEx6     = NULL;
	packetBuilderPromisc = NULL;

	stopStopStop = false;

	igmpMaxResponseTime = 10;

	embeddedMode = false;
}

ScanMethod::~ScanMethod(void)
{
	DestroyPackets();
}

//---------------------------------------------------------------------------

bool ScanMethod::CreatePackets(void)
{
	int i;

	DestroyPackets();

	if(scanPackCnt<=0) return false;

	try{
		packets = new ScanPacket[scanPackCnt];
	}
	catch(...){
		packets = NULL;
	}
	if(packets==NULL) return false;

	for(i=0; i<scanPackCnt; i++)
		packets[i].Initialize(false);

	return true;
}

void ScanMethod::DestroyPackets(void)
{
	if(packets!=NULL){
		delete[] packets;
		packets = NULL;
	}
}

ScanPacket* ScanMethod::FindPacket(DWORD addrIn, WORD portIn, WorkMethodType wmtIn)
{
	int i;
	ScanPacket* pack;

	if(packets==NULL)  return NULL;
	if(scanPackCnt<=0) return NULL;

	pack = NULL;
	for(i=0; i<scanPackCnt && pack==NULL; i++){
		if(packets[i].used){
/*
			CString str;
			str.Format(_T("   --- %x %x\n"), addrIn, packets[i].addr);
			OutputDebugString(str);
*/
			if(packets[i].addr==addrIn)
				if(packets[i].port==portIn)
					if(packets[i].methodType==wmtIn)
						pack = &packets[i];
		}
	}

	return pack;
}

bool ScanMethod::IsSameAddr6(in_addr6* iaIn1, in_addr6* iaIn2)
{
	if(iaIn1->u.Word[0] != iaIn2->u.Word[0]) return false;
	if(iaIn1->u.Word[1] != iaIn2->u.Word[1]) return false;
	if(iaIn1->u.Word[2] != iaIn2->u.Word[2]) return false;
	if(iaIn1->u.Word[3] != iaIn2->u.Word[3]) return false;
	if(iaIn1->u.Word[4] != iaIn2->u.Word[4]) return false;
	if(iaIn1->u.Word[5] != iaIn2->u.Word[5]) return false;
	if(iaIn1->u.Word[6] != iaIn2->u.Word[6]) return false;
	if(iaIn1->u.Word[7] != iaIn2->u.Word[7]) return false;

	return true;
}

ScanPacket* ScanMethod::FindPacket6(in_addr6* addrIn, WORD portIn, WorkMethodType wmtIn)
{
	int i;
	ScanPacket* pack;

	if(addrIn==NULL)   return NULL;
	if(packets==NULL)  return NULL;
	if(scanPackCnt<=0) return NULL;

	pack = NULL;
	for(i=0; i<scanPackCnt && pack==NULL; i++){
		if(packets[i].used){
/*
			CString str;
			str.Format(_T("   --- %x %x\n"), addrIn, packets[i].addr);
			OutputDebugString(str);
*/
			if(IsSameAddr6(addrIn, &(packets[i].addr6)))
				if(packets[i].port==portIn)
					if(packets[i].methodType==wmtIn)
						pack = &packets[i];
		}
	}

	return pack;
}

ScanPacket* ScanMethod::FindFirstUnusedPacket(void)
{
	int i;
	ScanPacket* pack;

	if(packets==NULL)  return NULL;
	if(scanPackCnt<=0) return NULL;

	pack = NULL;
	for(i=0; i<scanPackCnt && pack==NULL; i++){
		if(!packets[i].used)
			pack = &packets[i];
	}

	return pack;
}

int ScanMethod::PacketsUsed(void)
{
	int i, cnt;

	if(packets==NULL)  return 0;
	if(scanPackCnt<=0) return 0;

	cnt = 0;
	for(i=0; i<scanPackCnt; i++){
		if(packets[i].used)
			cnt++;
	}

	return cnt;
}

//---------------------------------------------------------------------------

DWORD ScanMethod::ComputeDeltaTime(DWORD dsIn, DWORD de)
{
DWORD ds, res;

	ds = dsIn;

	if(de<ds){
		// tick count has overlapped
		res = de;
		while(ds>0){
			ds++;
			res++;
		}
	}
	else{
		res = de - ds;
	}
	return res;
}

//---------------------------------------------------------------------------

int ScanMethod::InternalInit(void)
{
	return MSR_ERR_OK;
}

int ScanMethod::InternalExecute(void)
{
	return MSR_ERR_OK;
}

int ScanMethod::InternalDeath(ScanPacket*)
{
	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

void ScanMethod::FillRandomBuffer(void)
{
	int i;

	for(i=0; i<RANDOM_VALUES_COUNT; i++)
		randomVal[i] = mt19937.RandomMT();

	rndIdx = 0;
}

DWORD ScanMethod::GetRandomValue(void)
{
	DWORD retVal;

	if(rndIdx<0 || rndIdx>=RANDOM_VALUES_COUNT){
		FillRandomBuffer();
		rndIdx = 0;
	}

	retVal = randomVal[rndIdx];
	rndIdx++;
	return retVal;
}

void ScanMethod::ComputeRandomValues(void)
{
}

//---------------------------------------------------------------------------
int ScanMethod::Start(ScanStartParameters *ssp)
{
	int retVal;

	stopStopStop = false;

	abortReason = MSR_ERR_OK;

	if(ssp==NULL) return MSR_ERR_PARAMETER;

	if(ssp->sspWinPcap==NULL)              return MSR_ERR_PARAMETER;
	if(ssp->sspPackBuilderEx==NULL)        return MSR_ERR_PARAMETER;
	if(ssp->sspPackBuilderEx6==NULL)       return MSR_ERR_PARAMETER;
	if(ssp->sspPacketBuilderPromisc==NULL) return MSR_ERR_PARAMETER;
	if(ssp->sspWorkCfg==NULL)              return MSR_ERR_PARAMETER;
	if(ssp->sspWorkCfg->xMaxSimScan<=0)	   return MSR_ERR_PARAMETER;
	if(ssp->sspArpCacheList==NULL)         return MSR_ERR_PARAMETER;
	if(ssp->sspListOfAddr==NULL)		   return MSR_ERR_PARAMETER;
	if(ssp->sspMachInfoList==NULL)		   return MSR_ERR_PARAMETER;

	winPcap              = ssp->sspWinPcap;
	packetBuilderEx      = ssp->sspPackBuilderEx;
	packetBuilderEx6     = ssp->sspPackBuilderEx6;
	packetBuilderPromisc = ssp->sspPacketBuilderPromisc;
	config               = ssp->sspWorkCfg;
	arpCacheList         = ssp->sspArpCacheList;
	addrList             = ssp->sspListOfAddr;
	workPhase            = ssp->sspWorkPhase;
	machInfoList         = ssp->sspMachInfoList;
	hWnd                 = ssp->sspMsgWnd;
	scanParameters       = ssp->sspData;

	notifTcp             = ssp->sspNotifTcp;
	notifUdp             = ssp->sspNotifUdp;
	notifPing            = ssp->sspNotifPing;
	notifNetBios         = ssp->sspNotifNetBios;

	myMimic              = config->xMimicOS;

	wmiWorker            = ssp->sspWmiWorker;

	packetBuilderEx->SetMimic(myMimic);
	packetBuilderEx6->SetMimic(myMimic);

	switch(myType){
		case __wmtDns :
			scanPackCnt = config->xMaxSimScanDns;
			break;

		case __wmtUdpSend :
		case __wmtIpProtocol :
		case __wmt6UdpSend :
		case __wmt6Proto :
			scanPackCnt = config->xMaxSimScanIcmp;
			break;

		//case __wmtPing :
		//case __wmt6Ping :
		default :
			scanPackCnt = config->xMaxSimScan;
			break;
	}

	if(!CreatePackets()) return MSR_ERR_FAILED;

	addrIdx  = 0;
	portIdx  = 0;

	packSent = 0;
	packReceived = 0;
	packExpired = 0;

	retVal = InternalInit();
	if(retVal!=MSR_ERR_OK){
		abortReason = retVal;
		DestroyPackets();
		return MSR_ERR_OK;
	}

	lastScanTime = 0;

	if(!embeddedMode){
		if(!winPcap->BeginCapture(1)){
			DestroyPackets();
			return MSR_ERR_FAILED;
		}
	}

	retVal = InternalExecute();

	if(!embeddedMode){
		winPcap->EndCapture();
	}

	DestroyPackets();

	return retVal;
}

void ScanMethod::Stop(void)
{
	stopStopStop = true;
}

//---------------------------------------------------------------------------

MMachineInfo* ScanMethod::FindMachineByIp(DWORD addrIn)
{
	MMachineInfo* mmi;
	int i, cnt;
	bool found;

	if(machInfoList==NULL) return NULL;

	mmi = NULL;
	found = false;
	cnt = machInfoList->GetSize();
	for(i=0; i<cnt && !found; i++){
		mmi = machInfoList->GetByIdx(i);
		if(mmi!=NULL)
			found = mmi->address.addrIP.GetD()==addrIn;
	}

	return found ? mmi : NULL;
}

MMachineInfo* ScanMethod::FindMachineByIp6(in_addr6* ia6In)
{
	MMachineInfo* mmi;
	int i, cnt;
	bool found;

	if(machInfoList==NULL) return NULL;
	if(ia6In==NULL) return NULL;

	mmi = NULL;
	found = false;
	cnt = machInfoList->GetSize();
	for(i=0; i<cnt && !found; i++){
		mmi = machInfoList->GetByIdx(i);
		if(mmi!=NULL)
			found = mmi->address.addrIPv6.IsEqualWith(ia6In);
	}

	return found ? mmi : NULL;
}

//---------------------------------------------------------------------------

int ScanMethod::BuildPortList(bool asPortIn)
{
	portList.Clear();

	if(!portList.LoadFromString(scanParameters, asPortIn))
		return MSR_ERR_PARAMETER;

	switch(config->xScanOrder){
		case __soNumeric :
			portList.SortTheList();
			break;
		case __soRandomized :
			portList.RandomizeList();
			break;
	}

	if(hWnd!=NULL && !embeddedMode){
		if(myType==__wmtIpProtocol || myType==__wmt6Proto)
			SendMessage(hWnd, CHS_MSG_LIST_BUILDED, (WPARAM)2, (LPARAM)portList.GetSize());
		else
			SendMessage(hWnd, CHS_MSG_LIST_BUILDED, (WPARAM)1, (LPARAM)portList.GetSize());
	}

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

int ScanMethod::GetAddressCount(void)
{
	int cnt;

	if(workPhase==__wptDiscovery) cnt = addrList->GetSize();
	else                          cnt = machInfoList->GetSize();

	return cnt;
}

MFullAddress* ScanMethod::GetNextAddress(void)
{
	MFullAddress *result;
	MFullAddress *mfa;
	MMachineInfo* mmi;

	result = NULL;

	if(workPhase==__wptDiscovery){
		mfa = addrList->GetByIdx(addrIdx);
		if(mfa!=NULL)
			result = mfa;
	}
	else{
		mmi = machInfoList->GetByIdx(addrIdx);
		if(mmi!=NULL)
			result = &mmi->address;
	}

	return result;
}

bool ScanMethod::GetNextPair(ScanPair* result, ScanPairType pairType)
{
	bool res;

	switch(pairType){
		case __sptIpOnly :
			res = GetNextPairAddrOnly(result, pairType);
			break;

		case __sptIpAndPort :
			res = GetNextPairByPort(result, pairType);
			break;

		case __sptIpAndPromisc :
			res = GetNextPairByPromisc(result, pairType);
			break;

		default:
			res = false;
	}

	return res;
}

bool ScanMethod::GetNextPairAddrOnly(ScanPair* result, ScanPairType pairType)
{
	if(result==NULL) return false;

	if(addrIdx>=GetAddressCount()){
		// I have reached the end
		return false;
	}

	result->addr = GetNextAddress();
	if(result->addr==NULL) return false;

	result->port = 0;
	addrIdx++;

	return true;
}

bool ScanMethod::GetNextPairByAddr(ScanPair* result, ScanPairType pairType)
{
	WORD portL;

	if(result==NULL) return false;

	if(addrIdx>=GetAddressCount()){
		// I have reached the end
		return false;
	}

	result->addr = GetNextAddress();
	if(result->addr==NULL) return false;

	portL = portList.GetByIdx(portIdx);
	result->port = portL;

	portIdx++;
	if(portIdx>=portList.GetSize()){
		addrIdx++;
		portIdx = 0;
	}

	return true;
}

bool ScanMethod::GetNextPairByPort(ScanPair* result, ScanPairType pairType)
{
	WORD portL;

	if(result==NULL) return false;

	if(portIdx>=portList.GetSize()){
		// I have reached the end
		return false;
	}

	portL = portList.GetByIdx(portIdx);
	result->port = portL;

	result->addr = GetNextAddress();
	if(result->addr==NULL) return false;

	addrIdx++;
	if(addrIdx>=GetAddressCount()){
		portIdx++;
		addrIdx = 0;
	}

	return true;
}

bool ScanMethod::GetNextPairByPromisc(ScanPair* result, ScanPairType pairType)
{
	WORD portL;

	if(result==NULL) return false;

	switch(portIdx){
		case 0 :
			portL = (WORD)__pptBcast47;
			break;
		case 1 :
			portL = (WORD)__pptBcast16;
			break;
		case 2 :
			portL = (WORD)__pptBcast8;
			break;
		case 3 :
			portL = (WORD)__pptGroup;
			break;
		case 4 :
			portL = (WORD)__pptMcast0;
			break;
		case 5 :
			portL = (WORD)__pptMcast1;
			break;

		default :
			// I have reached the end
			return false;
	}
	result->port = portL;

	result->addr = GetNextAddress();
	if(result->addr==NULL) return false;

	addrIdx++;
	if(addrIdx>=GetAddressCount()){
		portIdx++;
		addrIdx = 0;
	}

	return true;
}

//---------------------------------------------------------------------------

void ScanMethod::CheckDeaths(void)
{
	DWORD cTime, elapsed, maxTime;
	int i;

	if(myType==__wmtIgmp){
		maxTime = (DWORD)igmpMaxResponseTime;
		maxTime *= 100;	// it is in tenths of a second
		maxTime += 500; // add a half of second to correct computation roundings
	}
	else{
		maxTime = (DWORD)config->xMaxWaitTime;
	}

	cTime = GetTickCount();

	for(i=0; i<scanPackCnt; i++){
		if(packets[i].used){
			elapsed = ComputeDeltaTime(packets[i].sTime, cTime);
			if(elapsed>=maxTime){
/*
				CString str;
				in_addr ia;
				ia.S_un.S_addr = packets[i].addr;
				str.Format(_T("   > Time out receving ARP Replay for %d.%d.%d.%d\n"),
					ia.S_un.S_un_b.s_b1, ia.S_un.S_un_b.s_b2, ia.S_un.S_un_b.s_b3, ia.S_un.S_un_b.s_b4);
				OutputDebugString(str);
*/
				packExpired++;

				InternalDeath(&packets[i]);

				packets[i].Initialize(false);
			}
		}
	}
}

//---------------------------------------------------------------------------

bool ScanMethod::SendPacket(BYTE* dataIn, int sizeIn)
{
	bool res;
	if(dataIn==NULL)  return false;
	if(sizeIn<=0)     return false;
	if(winPcap==NULL) return false;

	res = winPcap->SendPacket(dataIn, sizeIn);
	if(res)
		packSent++;

	return res;
}

//---------------------------------------------------------------------------

int ScanMethod::ReadPacket(void)
{
	if(winPcap==NULL) return -3;

	capPackSize = MAX_CAPTURED_PACKET_SIZE;

	return winPcap->CaptureOnePacket(&capPacket[0], &capPackSize);
}

bool ScanMethod::CheckResponses(void)
{
	EthernetHeaderPG *eth;
	WORD lenType;
	int res;
	bool otherProcessed;

	res = ReadPacket();
	if(res<0)
		return false;

	if(res==0){
		// time out
		return true;
	}

	// packet received

	if(capPackSize<34){
		// incomplete packet
		// eth + ip   = 14 + 20 = 34
		// eth + ipv6 = 14 + 40 = 54
		// eth + arp  = 14 + 28 = 42
		// eth + ipx  = 14 + 30 = 44 
		return true;
	}

	// process packet
	eth = (EthernetHeaderPG*)&capPacket[0];
	lenType = ntohs(eth->lenType);
	switch(lenType){
		case 0x0800:
			// IP
			ProcessIpPacket(&capPacket[14], capPackSize - 14, &capPacket[6]);
			break;

		case 0x86DD :
			// IPv6
			ProcessIPv6Packet(&capPacket[14], capPackSize - 14, &capPacket[6]);
			break;

		case 0x0806:
			// ARP
			ProcessArpAny(capPacket, capPackSize);

			res = memcmp(&capPacket[12], &arpReplySignature[0], 10);
			if(res==0){
				// this seems to be an ARP Reply packet
				// requested MAC is at &capPacket[22]
				ProcessArpReply(&capPacket[14], capPackSize - 14);
			}
			break;

		default:
			otherProcessed = false;
			if(lenType<0x600)		// IEEE 802.3 Frame
				if(capPackSize>14)	// 14 = sizeof(EthernetHeaderPG)
					otherProcessed = Process802Dot3Frame(lenType, &capPacket[14], capPackSize - 14, &capPacket[6], &capPacket[0]);

			if(!otherProcessed)
				ProcessOtherEthernetPacket(lenType, &capPacket[14], capPackSize - 14, &capPacket[6]);
			break;
	}

	return true;
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessIpPacket(BYTE* dataIn, int sizeIn, BYTE* srcMAC)
{
	IPHeaderPG *iph;

	if(sizeIn<20){
		// incomplete IP packet
		return;
	}

	iph = (IPHeaderPG*)dataIn;
	switch(iph->Protocol){
		case IPPROTO_ICMP :
			ProcessIcmp(dataIn, sizeIn, srcMAC);
			break;

		case IPPROTO_IGMP :
			ProcessIgmp(dataIn, sizeIn, srcMAC);
			break;

		case IPPROTO_TCP :
			ProcessTcp(dataIn, sizeIn, srcMAC);
			break;

		case IPPROTO_UDP :
			ProcessUdp(dataIn, sizeIn);
			ProcessUdpEx(dataIn, sizeIn, srcMAC);
			break;

		default:
			ProcessOtherIpPacket(dataIn, sizeIn, srcMAC);
			break;
	}
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessIcmp(BYTE* dataIn, int sizeIn, BYTE* srcMAC)
{
	IPHeaderPG *iph;
	ICMPHeaderPG* icmph;
	int dataOffset;
	BYTE* newPacket;
	int   newSize;

	iph = (IPHeaderPG*)dataIn;
	dataOffset  = (int)(iph->HLV & 0x0F);
	dataOffset *= 4;
	if(sizeIn<(dataOffset + 8)){
		// incomplete packet
		return;
	}

	icmph = (ICMPHeaderPG*)&dataIn[dataOffset];

	ProcessIcmpAny(dataIn, sizeIn, srcMAC);

	switch(icmph->Type){
		case 0 :
			// Echo Reply
			ProcessEchoReply(dataIn, sizeIn);
			ProcessEchoReplyEx(dataIn, sizeIn, srcMAC);
			break;

		case 3 :
			// Destination unreacheable
			switch(icmph->Code){
				case 1 :
					// Type = destination unreacheable
					// Code = host unreacheable
					// Data = Original IP Header + ICMP Header (Original IP Header + 64 bits of Original Data Datagram)
					newPacket = &dataIn[dataOffset + 8];
					newSize   = sizeIn - dataOffset - 8;
					ProcessHostUnreacheable(newPacket, newSize);
					break;

				case 2:
					// Type = destination unreacheable
					// Code = protocol unreachable
					// Data = Original IP Header + 64 bits of Original Data Datagram
					newPacket = &dataIn[dataOffset + 8];
					newSize   = sizeIn - dataOffset - 8;
					ProcessProtocolUnreacheable(newPacket, newSize);
					break;

				case 3:
					// Type = destination unreacheable
					// Code = port unreachable
					// Data = Original IP Header + 64 bits of Original Data Datagram
					newPacket = &dataIn[dataOffset + 8];
					newSize   = sizeIn - dataOffset - 8;
					ProcessPortUnreacheable(newPacket, newSize);
					break;
			}
			break;
	}
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessArpAny(BYTE*, int)
{
}

void ScanMethod::ProcessArpReply(BYTE*, int)
{
}

void ScanMethod::ProcessEchoReply(BYTE*, int)
{
}

void ScanMethod::ProcessEchoReplyEx(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessHostUnreacheable(BYTE*, int)
{
}

void ScanMethod::ProcessProtocolUnreacheable(BYTE*, int)
{
}

void ScanMethod::ProcessPortUnreacheable(BYTE*, int)
{
}

void ScanMethod::ProcessIcmpAny(BYTE*, int, BYTE*)
{
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessIgmp(BYTE*, int, BYTE*)
{
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessTcp(BYTE*, int, BYTE*)
{
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessUdp(BYTE*, int)
{
}

void ScanMethod::ProcessUdpEx(BYTE*, int, BYTE*)
{
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessOtherIpPacket(BYTE*, int, BYTE*)
{
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessIPv6Packet(BYTE* dataIn, int sizeIn, BYTE* srcMAC)
{
	IP6HeaderPG *iph;
	HopByHopHeaderPG *hbhh;

	BYTE  nextHeader;
	BYTE* nextPacket;
	int   nextSize;

	int sizeofIPv6header;
	int sizeofHeader;

	bool done;

	if(sizeIn<40){
		// incomplete IPv6 packet
		return;
	}

	sizeofIPv6header = sizeof(IP6HeaderPG);

	iph = (IP6HeaderPG*)dataIn;
	if(IPv6HdrVersion(iph->VTF)!=0x06){
		// not an IPv6 packet
		return;
	}

	nextHeader = iph->NextHeader;
	nextPacket = &dataIn[sizeofIPv6header];
	nextSize   = sizeIn - sizeofIPv6header;

	receivedHeaders = 0;

	done = false;
	while(!done){
		switch(nextHeader){
			case 0x00 :		// Hop-By-Hop option header
				hbhh = (HopByHopHeaderPG*)&dataIn[sizeofIPv6header];
				if(hbhh->HdrExtLen==255){
					done = true;
					break;
				}
				sizeofHeader = 8 * (hbhh->HdrExtLen + 1);

				receivedHeaders |= RCV_HDR_HOPBYHOP;

				nextHeader  = hbhh->NextHeader;
				nextPacket  = &nextPacket[sizeofHeader];
				nextSize   -= sizeofHeader;
				if(nextSize<=0)
					done = true;
				break;

			case 0x3A :		// ICMPv6
				ProcessIcmp6(nextPacket, nextSize, iph->Source, srcMAC);
				done = true;
				break;

			case IPPROTO_TCP :		// TCPv6
				ProcessTcp6(nextPacket, nextSize, iph->Source, srcMAC);
				done = true;
				break;

			case IPPROTO_UDP :		// UDPv6
				ProcessUdp6(nextPacket, nextSize, iph->Source, srcMAC);
				done = true;
				break;

			default:
				ProcessOther6Packet(nextPacket, nextSize, iph->Source, srcMAC);
				done = true;
				break;
		}
	}
}

void ScanMethod::ProcessIcmp6(BYTE* dataIn, int sizeIn, BYTE* srcIP, BYTE* srcMAC)
{
	ICMP6HeaderPG *icmph;
	BYTE* newPacket;
	int   newSize;

	if(sizeIn<4) return;

	icmph = (ICMP6HeaderPG*)dataIn;

	ProcessIcmp6Any(dataIn, sizeIn, srcIP, srcMAC);

	switch(icmph->Type){
		case 1 :		// Destination Unreachable (RFC 2463 + RFC 4443)
			switch(icmph->Code){
				case 0 :	// no route to destination
				case 1 :	// communication with destination administratively prohibited
				case 2 :	// beyond scope of source address (RFC 4443)
				case 3 :	// address unreachable
				case 5 :	// source address failed ingress/egress policy (RFC 4443)
				case 6 :	// reject route to destination (RFC 4443)
					newPacket = &dataIn[8];
					newSize   = sizeIn - 8;
					ProcessDestinationUnreachable6(newPacket, newSize, srcIP);
					break;
				case 4 :	// port unreachable
					newPacket = &dataIn[8];
					newSize   = sizeIn - 8;
					ProcessPortUnreachable6(newPacket, newSize, srcIP);
					break;
			}
			break;

		case 4 :		// Parameter Problem (RFC 2463)
			switch(icmph->Code){
				case 0 :	// erroneous header field encountered
					ProcessErrHeaderField(dataIn, sizeIn, srcIP, srcMAC);
					break;
				case 1 :	// unrecognized Next Header type encountered
					ProcessUnkNextHeader(dataIn, sizeIn, srcIP, srcMAC);
					break;
				case 2 :	// unrecognized IPv6 option encountered
					ProcessUnkIPv6Option(dataIn, sizeIn, srcIP, srcMAC);
					break;
			}
			break;

		case 129 :		// Echo Reply (RFC 2463)
			ProcessEchoReply6(dataIn, sizeIn, srcIP, srcMAC);
			break;

		case 130 :		// Multicast Listener Query (RFC 2710)
			break;
		case 131 :		// Multicast Listener Report (RFC 2710)
			ProcessMulticastListenerReport(dataIn, sizeIn, srcIP, srcMAC);
			break;
		case 132 :		// Multicast Listener Done (RFC 2710)
			break;

		case 133 :		// Router Solicitation Message (RFC 2461)
			break;
		case 134 :		// Router Advertisement Message (RFC 2461)
			ProcessRouterAdvertisement(dataIn, sizeIn, srcIP, srcMAC);
			break;

		case 135 :		// Neighbor Solicitation Message (RFC 2461)
			break;
		case 136 :		// Neighbor Advertisement Message (RFC 2461)
			ProcessNeighborAdvertisement(dataIn, sizeIn, srcIP, srcMAC);
			break;

		case 137 :		// Redirect Message (RFC 2461)
			break;
	}
}

void ScanMethod::ProcessDestinationUnreachable6(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessPortUnreachable6(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessErrHeaderField(BYTE*, int, BYTE*, BYTE*)
{
}
void ScanMethod::ProcessUnkNextHeader(BYTE*, int, BYTE*, BYTE*)
{
}
void ScanMethod::ProcessUnkIPv6Option(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessEchoReply6(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessMulticastListenerReport(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessRouterAdvertisement(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessNeighborAdvertisement(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessIcmp6Any(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessTcp6(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessUdp6(BYTE*, int, BYTE*, BYTE*)
{
}

void ScanMethod::ProcessOther6Packet(BYTE*, int, BYTE*, BYTE*)
{
}

//---------------------------------------------------------------------------

bool ScanMethod::Process802Dot3Frame(WORD lenTypeIn, BYTE* dataIn, int sizeIn, BYTE* srcMAC, BYTE* dstMAC)
{
	LLCHeaderModPG *llcmh;
	SNAPHeaderPG   *snaph;
	MAddressMAC    macCmp;
	WORD *wVal;
	WORD pid;

	if(sizeIn<3) return false;		// LLCHeaderPG

	llcmh = (LLCHeaderModPG*)dataIn;

	if(llcmh->DSAPSSAP==0x4242){
		macCmp.LoadFromBytePtr(dstMAC);

		if(macCmp.IsGmrpDestinationMAC()){
			ProcessGMRP(&dataIn[3], sizeIn - 3, srcMAC);
			return true;
		}
		if(macCmp.IsGvrpDestinationMAC()){
			ProcessGVRP(&dataIn[3], sizeIn - 3, srcMAC);
			return true;
		}

		if(macCmp.IsStpDestinationMAC()){
			ProcessSTP(&dataIn[3], sizeIn - 3, srcMAC);
			return true;
		}

		return false;
	}

	if(llcmh->DSAPSSAP==0xE0E0){
		// IEEE 802.2 IPX/SPX
		// IPX/SPX does have only the LLC part (the first 3 bytes) of the LLCSNAPHeaderPG !
		ProcessIPXorSPX(&dataIn[3], sizeIn - 3, srcMAC);
		return true;
	}

	if(sizeIn<8) return false;		// LLCHeaderPG + SNAPHeaderPG

	if(llcmh->DSAPSSAP==0xAAAA){
		// SNAP Frame
		snaph = (SNAPHeaderPG*)&dataIn[3];

		wVal = (WORD*)&dataIn[6];
		pid = ntohs(*wVal);

		switch(pid){
			case 0x010B:
				// 01:00::0c:cc:cc:cd
				ProcessSTP(&dataIn[8], sizeIn - 8, srcMAC);
				return true;

			case 0x2000: // Cisco Discovery Protocol
				// 01:00::0c:cc:cc:cc
				ProcessCDP(&dataIn[8], sizeIn - 8, srcMAC);
				return true;

			case 0x2003: // VLAN Trunknig Protocol
				// 01:00::0c:cc:cc:cc
//				return true;
				break;

			case 0x2004: // Dynamic Trunking Protocol
				// 01:00::0c:cc:cc:cc
				ProcessDTP(&dataIn[8], sizeIn - 8, srcMAC);
				return true;
		}
	}

	return false;
}

//---------------------------------------------------------------------------

void ScanMethod::ProcessIPXorSPX(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessSTP(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessGVRP(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessGMRP(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessCDP(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessDTP(BYTE*, int, BYTE*)
{
}

void ScanMethod::ProcessOtherEthernetPacket(WORD, BYTE*, int, BYTE*)
{
}

//---------------------------------------------------------------------------

void ScanMethod::ClearRespReceivedFlag(void)
{
	MMachineInfo* mmi;
	int i, cnt;

	if(workPhase!=__wptScan) return;
	if(machInfoList==NULL) return;

	cnt = machInfoList->GetSize();
	for(i=0; i<cnt; i++){
		mmi = machInfoList->GetByIdx(i);
		if(mmi!=NULL)
			mmi->address.respReceived = false;
	}
}

void ScanMethod::CheckRespReceivedFlag(void)
{
	MMachineInfo* mmi;
	int i, cnt;

	if(workPhase!=__wptScan) return;
	if(machInfoList==NULL) return;

	cnt = machInfoList->GetSize();
	for(i=0; i<cnt; i++){
		mmi = machInfoList->GetByIdx(i);
		if(mmi!=NULL){
			if(mmi->address.respReceived){
				SetMachineInfoRespFlag(mmi);
			}
		}
	}
}

void ScanMethod::SetMachineInfoRespFlag(MMachineInfo* mmi)
{
	if(mmi==NULL) return;

	switch(myType){
		case __wmtFin :
			mmi->recvFin = true;
			break;
		case __wmtNull :
			mmi->recvNull = true;
			break;
		case __wmtXmas :
			mmi->recvXmas = true;
			break;

		case __wmtUdpSend :
			mmi->recvUdpSend = true;
			break;

		case __wmtIpProtocol :
			mmi->recvIpProto = true;
			break;

		case __wmt6Fin :
			mmi->recv6Fin = true;
			break;

		case __wmt6Null :
			mmi->recv6Null = true;
			break;

		case __wmt6Xmas :
			mmi->recv6Xmas = true;
			break;

		case __wmt6UdpSend :
			mmi->recv6UdpSend = true;
			break;

		case __wmt6Proto :
			mmi->recv6NextHeader = true;
			break;
	}
}

//---------------------------------------------------------------------------

void ScanMethod::SetEmbeddedMode(bool embeddedModeIn)
{
	embeddedMode = embeddedModeIn;
}

//---------------------------------------------------------------------------
